package com.study.sbsummary.config.database;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.study.sbsummary.config.database.shadow.ShadowInterceptor;
import com.study.sbsummary.context.RequestContext;
import com.study.sbsummary.utils.IOUtil;
import com.study.sbsummary.utils.ThreadUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Configuration
@Data
@Slf4j
public class InterceptorConfig {
    private static final String[] types = new String[]{"TABLE", "VIEW"};

    //ConcurrentHashMap读不加锁，写加锁
    private static final Map<String, TableNameHandler> dynamicTblHanders = new ConcurrentHashMap<>();

    private static final Map<String, String> dynamicTbls = new ConcurrentHashMap<>();

    @Autowired
    private TableNameHandler tableNameHandler;

    @Autowired
    private ShadowInterceptor shadowInterceptor;

    @Autowired(required = false)
    private Interceptor[] interceptors;

    @Bean
    @ConditionalOnBean({ShadowInterceptor.class})
    public MybatisPlusInterceptor mybatisPlusInterceptor(@Autowired ShadowInterceptor shadowInterceptor,
                                                         DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor) {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        if (shadowInterceptor.isShadowTable()) {
            //第一种方案
            interceptor.addInnerInterceptor(shadowInterceptor);

            //第二种方案：某些场景不可用
//        interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);
        }

        return interceptor;
    }

    @Bean
    public DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor() {
        DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();
        dynamicTableNameInnerInterceptor.setTableNameHandlerMap(dynamicTblHanders);
        return dynamicTableNameInnerInterceptor;
    }

    @Bean
    public TableNameHandler tableNameHandler() {
        return new TruelyTableNameHandler();
    }

    public static class TruelyTableNameHandler implements TableNameHandler {

        @Override
        public String dynamicTableName(String sql, String tableName) {
            if (RequestContext.isOnlineTest()) {
                return dynamicTbls.get(tableName);
            } else {
                return tableName;
            }
        }
    }

    public void loadMetadata(String name, SqlSessionFactory sqlSessionFactory) {
        for (int i = 0; i < 3; i++) {
            SqlSession sqlSession = null;
            Connection connection = null;
            try {
                sqlSession = sqlSessionFactory.openSession();
                connection = sqlSession.getConnection();
                DatabaseMetaData metaData = connection.getMetaData();
                ResultSet rs = metaData.getTables(connection.getCatalog(), null, "%", types);
                while (rs.next()) {
                    String tbl = rs.getString(3);
                    log.info("tableName: {}, session: {}", tbl, name);
                    if (ShadowInterceptor.isShadowName(tbl)) {
                        continue;
                    }
                    dynamicTblHanders.put(tbl, tableNameHandler);
                    dynamicTbls.put(tbl, ShadowInterceptor.shadowName(tbl));
                }
                break;
            } catch (Exception e) {
                log.warn("failed to load metadata of session {}, due to {}", name, e);
                if (e instanceof IllegalArgumentException) {
                    break;
                }
                ThreadUtil.sleep(1000);
            } finally {
                IOUtil.close(connection);
                IOUtil.close(sqlSession);
            }
        }
    }
}
